Crate naga[][src]

Expand description

Universal shader translator.

The central structure of the crate is Module. A Module contains:

  • Functions, which have arguments, a return type, local variables, and a body,

  • EntryPoints, which are specialized functions that can serve as the entry point for pipeline stages like vertex shading or fragment shading,

  • Constants and GlobalVariables used by EntryPoints and Functions, and

  • Types used by the above.

The body of an EntryPoint or Function is represented using two types:

  • An Expression produces a value, but has no side effects or control flow. Expressions include variable references, unary and binary operators, and so on.

  • A Statement can have side effects and structured control flow. Statements do not produce a value, other than by storing one in some designated place. Statements include blocks, conditionals, and loops, but also operations that have side effects, like stores and function calls.

Statements form a tree, with pointers into the DAG of Expressions.

Restricting side effects to statements simplifies analysis and code generation. A Naga backend can generate code to evaluate an Expression however and whenever it pleases, as long as it is certain to observe the side effects of all previously executed Statements.

Many Statement variants use the Block type, which is Vec<Statement>, with optional span info, representing a series of statements executed in order. The body of an EntryPoints or Function is a Block, and Statement has a Block variant.

Arenas

To improve translator performance and reduce memory usage, most structures are stored in an Arena. An Arena<T> stores a series of T values, indexed by Handle<T> values, which are just wrappers around integer indexes. For example, a Function’s expressions are stored in an Arena<Expression>, and compound expressions refer to their sub-expressions via Handle<Expression> values. (When examining the serialized form of a Module, note that the first element of an Arena has an index of 1, not 0.)

Function Calls

Naga’s representation of function calls is unusual. Most languages treat function calls as expressions, but because calls may have side effects, Naga represents them as a kind of statement, Statement::Call. If the function returns a value, a call statement designates a particular Expression::CallResult expression to represent its return value, for use by subsequent statements and expressions.

Expression evaluation time and scope

It is essential to know when an Expression should be evaluated, because its value may depend on previous Statements’ effects. But whereas the order of execution for a tree of Statements is apparent from its structure, it is not so clear for Expressions, since an expression may be referred to by any number of Statements and other Expressions.

Naga’s rules for when Expressions are evaluated are as follows:

  • Constant expressions are considered to be implicitly evaluated before execution begins.

  • FunctionArgument and LocalVariable expressions are considered implicitly evaluated upon entry to the function to which they belong. Function arguments cannot be assigned to, and LocalVariable expressions produce a pointer to the variable’s value (for use with Load and Store). Neither varies while the function executes, so it suffices to consider these expressions evaluated once on entry.

  • Similarly, GlobalVariable expressions are considered implicitly evaluated before execution begins, since their value does not change while code executes, for one of two reasons:

    • Most GlobalVariable expressions produce a pointer to the variable’s value, for use with Load and Store, as LocalVariable expressions do. Although the variable’s value may change, its address does not.

    • A GlobalVariable expression referring to a global in the StorageClass::Handle storage class produces the value directly, not a pointer. Such global variables hold opaque types like shaders or images, and cannot be assigned to.

  • A Call expression that is the result of a Statement::Call, representing the call’s return value, is evaluated when the Call statement is executed.

  • All other expressions are evaluated when the (unique) Statement::Emit statement that covers them is executed. The Expression::needs_pre_emit method returns true if the given expression is one of those variants that does not need to be covered by an Emit statement.

Each Expression has a scope, which is the region of the function within which it can be used by Statements and other Expressions. It is a validation error to use an Expression outside its scope.

An expression’s scope is defined as follows:

  • The scope of a Constant, GlobalVariable, FunctionArgument or LocalVariable expression covers the entire Function in which it occurs.

  • The scope of an expression evaluated by an Emit statement covers the subsequent expressions in that Emit, the subsequent statements in the Block to which that Emit belongs (if any) and their sub-statements (if any).

  • If a Call statement has a result expression, then that expression’s scope covers the subsequent statements in the Block to which that Call belongs (if any) and their sub-statements (if any).

For example, this implies that an expression evaluated by some statement in a nested Block is not available in the Block’s parents. Such a value would need to be stored in a local variable to be carried upwards in the statement tree.

Modules

Functions which export shader modules into binary and text formats.

Parsers which load shaders into memory.

Module processing functionality.

Structs

An arena holding some kind of component (e.g., type, constant, instruction, etc.) that can be referenced.

Memory barrier flags.

A code block is a vector of statements, with maybe a vector of spans.

Constant value.

Early fragment tests. In a standard situation if a driver determines that it is possible to switch on early depth test it will. Typical situations when early depth test is switched off:

The main function for a pipeline stage.

A function defined in the module.

A function argument.

Variable defined at module level.

A strongly typed reference to an arena item.

Variable defined at function level.

Shader module.

A strongly typed range of handles.

Pipeline binding information for global resources.

Flags describing an image.

Member of a user-defined structure.

A case for a switch statement.

A data type declared in the module.

Enums

Size of an array.

Function on an atomic value.

Operation that can be applied on two values.

Describes how an input/output variable is to be bound.

Built-in inputs and outputs.

Enables adjusting depth without disabling early Z.

Additional information, dependent on the kind of constant.

Axis on which to compute a derivative.

An expression that can be evaluated to obtain a value.

Sub-class of the image type.

The number of dimensions an image has.

Type of an image query.

The interpolation qualifier of a binding or struct field.

Built-in shader function for math.

Built-in shader function for testing relation between values.

Sampling modifier to control the level of detail.

The sampling qualifiers of a binding or struct field.

Primitive type for a scalar.

A literal scalar value, used in constants.

Stage of the programmable pipeline.

Instructions which make up an executable block.

Class of storage for variables.

Component selection for a vector swizzle.

Enum with additional information, depending on the kind of type.

Operation that can be applied on a single value.

Number of components in a vector.

Constants

Width of a boolean type, in bytes.

Type Definitions

Number of bytes per scalar.

Hash map that is faster but not resilient to DoS attacks.

Hash set that is faster but not resilient to DoS attacks.